实现这个效果的关键点只有两点:
- 获取正在输入内容在
UITextView
占用了多少行 - 让
UITextView
动态改变大小
一开始,为了解决第一个问题,我一直在考虑怎么获取换行事件,发现只用捕捉
\n
输入即可,但这样又有一个麻烦,就是删除行怎么办?所以感觉这个思路太麻烦
对于第一点,有一个方便的计算方法,就是获取UITextView
内容的高度比上UITextView
的字体的高度,即可得到当前的行数。
这里有一个技巧,那就是不要用UITextView
的bound.size.height
获取其高度,因为这个获取的高度跟你输入的文字总高度并没有任何关系。
要知道,UITextView
是继承UIScrollView
的。
NS_CLASS_AVAILABLE_IOS(2_0) @interface UITextView : UIScrollView
这一点你可以不知道,但根据平时的操作经验,一个UITextView
输入文字过多时是可以用手上下拖动浏览内容的,所以可以猜想是和UIScrollView
有关系。
另一个类似的输入框
UITextField
不支持换行哦,设置什么lineBreakMode
啊,lineNumber
(好像没这个属性)啊都不可以换行!我以前在这上面栽过跟头。UITextField
输入满了会把文字顶到前面看不见的地方,但还不支持左右拖动!这一点比Android
体验要差!
好了,知道其集成UIScrollView
就好办了,因为UITextView
只有在输入内容超过其显示范围才可以拖动,那就知道输入文字的bound
就是UIScrollView
的contentSize
。这样就知道输入内容的总大小了。
那每一行的高度呢?
按我以前的经验,行高差不多是字体大小的 4/3 倍.这样设值显示效果一般都挺好。所以可以设值一个宏定义来处理与字体相关的高度。
当然,我的经验是因为过去没有这方面经验的笨办法,正确的办法是使用Font
的lineHeight
属性:
// The height of text lines (measured in points). (read-only)
@property(nonatomic,readonly) CGFloat lineHeight NS_AVAILABLE_IOS(4_0);
OK,知道每行的高度,就可以计算当前有多少行文字了,在
- (void)textViewDidChange:(UITextView *)textView
代理方法中计算高度即可。
对于第二点,根据计算出的高度重绘界面布局即可。这里我用的是自动布局,每次高度改变就刷新布局关系就行。下面放部分代码:
我把输入框和一些其他组件封装为一个控件,在类中声明代理:
@protocol EssayEditDelegate
@optional
- (void) onTextViewLineCountChangeTo:(NSInteger)lines;
@end
在每次输入内容的时候计算行数,声明一个变量保存上一次的行数,两次行数不一致即为发生换行
- (void)textViewDidChange:(UITextView *)textView {
NSInteger numLines = textView.contentSize.height / textView.font.lineHeight;
if (numLines != rows) {
rows = numLines;
//发生换行
if (_delegate && [_delegate respondsToSelector:@selector(onTextViewLineCountChangeTo:)]) {
[_delegate onTextViewLineCountChangeTo:rows];
}
}
......
}
在外部实现代理方法,更新界面
#pragma mark - Essay delegate
- (void)onTextViewLineCountChangeTo:(NSInteger)lines {
rowCount = lines;
[self remakeConstraint];
}
- (void)remakeConstraint {
NSInteger toobarHeight;
NSInteger rows = rowCount > DEFAULT_ROW_COUNT ? rowCount : DEFAULT_ROW_COUNT;
toobarHeight = (ESSAY_EDIT_FONT_SIZE / 2 * 3) * rows + MARGIN_BUTTON * 2;
[toolBarView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.bottom.right.equalTo(self);
make.top.equalTo(quesImageView.mas_bottom);
make.height.mas_equalTo(toobarHeight);
}];
}